#include "SourceContinue.hpp"

#include "Source.hpp"
#include "SourceAttenuee.hpp"

#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>

using namespace std;
using namespace boost;
using namespace boost::assign;

/**
 * Constructeur standard, initialise les champs de la classe mère, les
 * autres attributs étant initialisés pas la classe dérivée.
 *
 * @param[in] dimension dimension de la distribution (1 pour
 * linéraire, 2 pour surfacique).
 * @param[in] limites limites de la distribution (@see
 * SourceContinue::Limites).
 * @param[in] nombres nombre de sources à placer selon chaque
 * dimension.
 * @param[in] coul couleur de la source.
 * @param[in] c1 coefficient d'atténuation constante.
 * @param[in] c2 coefficient d'atténuation linéaire.
 * @param[in] c3 coefficient d'atténuation quadratique.
 */
SourceContinue::SourceContinue( const unsigned& dimension,
				const vector< Coord >& limites,
				const vector< unsigned >& nombres,
				const Couleur& coul,
				const double& c1, const double& c2,
				const double& c3 )
  : Dimension( dimension ), CouleurLumiere( coul ), Initialisation( false )
{
  Nombres.assign( nombres.begin(), nombres.end() );

  Coefficients = list_of( c1 )( c2)( c3 );

  if ( abs( c2 ) < 1.e-7 && abs( c3 ) < 1.e-7 )
    SrcType = srcPonctuelle;
  else
    SrcType = srcAttenuee;
}

/**
 * Constructeur de copie, qui permet de dupliquer une instance
 * héritant de SourceContinue.
 *
 * @param[in] src instance à copier.
 */
SourceContinue::SourceContinue( const SourceContinue& src )
  : Dimension( src.dimension() ), CouleurLumiere( src.CouleurLumiere ),
    SrcType( src.SrcType ), Initialisation( src.Initialisation )
{
  Nombres.assign( src.Nombres.begin(), src.Nombres.end() );

  Limites.assign( src.limitesPtr() -> begin(),
		  src.limitesPtr() -> end() );

  Coefficients.assign( src.Coefficients.begin(), src.Coefficients.end() );

  const vector< Source* > aCopier = src.elementsFinis();

  unsigned i, nbr( src.nombre() );

  for ( i = 0; i < nbr; i++ )
    {
      switch( SrcType )
	{
	case srcAttenuee:
	  ElementsFinis.push_back( new SourceAttenuee( *static_cast< SourceAttenuee *>( aCopier[ i ] ) ) );
	  break;
	default:
	  ElementsFinis.push_back( new Source( *aCopier[ i ] ) );
	}
    }
}

/**
 * Le destructeur doit libérer la mémoire allouée.
 */
SourceContinue::~SourceContinue()
{
  BOOST_FOREACH( Source* src, ElementsFinis )
    {
      delete src;
      src = 0;
    }

  ElementsFinis.clear();
}

/**
 * Permet d'accéder à la dimension de l'objet lumineux.
 *
 * @return une référence constante sur Dimension.
 */
const unsigned& SourceContinue::dimension() const
{
  return Dimension;
}

/**
 * Permet d'accéder au nombre de sources ponctuelles.
 *
 * @return le nombre de sources ponctuelles qui devront êtres ajoutées
 * à la scène.
 */
unsigned SourceContinue::nombre() const
{
  assert( Dimension == Nombres.size() );

  unsigned result( 1u );

  for ( unsigned i( 0 ); i < Dimension; i++ )
    result *= Nombres[ i ];

  return result;
}

/**
 * Permet d'accéder aux bornes de l'objet lumineux.
 *
 * @return une référence constante sur Limites.
 */
const vector< Coord >& SourceContinue::limites() const
{
  return Limites;
}

/**
 * Permet d'accéder à un pointeur sur les bornes de l'objet lumineux.
 *
 * @return l'adresse de Limites.
 */
const vector< Coord >* SourceContinue::limitesPtr() const
{
  return &Limites;
}

/**
 * Permet d'accéder aux sources lumineuses à ajouter à la scène.
 *
 * Si les sources n'ont pas encore été placées, alors on appelle
 * prepareReseauLumiere pour les créer.
 *
 * @return une référence constante sur ElementsFinis.
 */
const vector< Source* >& SourceContinue::elementsFinis() const
{
  if ( ! Initialisation )
    prepareReseauLumieres();

  return ElementsFinis;
}

/**
 * Permet d'accéder aux sources lumineuses à ajouter à la scène.
 *
 * Si les sources n'ont pas encore été placées, alors on appelle
 * prepareReseauLumiere pour les créer.
 *
 * @return un pointeur sur ElementsFinis.
 */
const vector< Source* >* SourceContinue::elementsFinisPtr() const
{
  if ( ! Initialisation )
    prepareReseauLumieres();

  return &ElementsFinis;
}

/**
 * Crée les sources lumineuses selon les paramètres voulus.
 *
 * On fait appel à la méthode virtuelle pure positionInterpolee pour
 * connaître la position de chaque source.
 *
 * Une fois toutes les sources créée, on change la valeur du booléen
 * Initialisation à \e true.
 */
void SourceContinue::prepareReseauLumieres() const
{
  Coord position;
  
  unsigned nbr( nombre() );
  
  for ( unsigned i( 0 ); i < nbr; i++ )
    {
      position = positionInterpolee( i );
      switch( SrcType )
	{
	case srcAttenuee:
	  ElementsFinis.push_back( new SourceAttenuee( CouleurLumiere, position,
						       Coefficients[ 0 ],
						       Coefficients[ 1 ],
						       Coefficients[ 2 ] ) );
	  break;
	default:
	  ElementsFinis.push_back( new Source( CouleurLumiere, position ) );
	}
    }

  Initialisation = true;
}
